﻿using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Model.Entity;
using Model.Entity.Members;
using Model.DataManagement;

namespace LibraryManagerTests.Model.DataManagement
{

    /// <summary>
    /// Subclassed to make internal fields available for testing.
    /// </summary>
    class TestableMemberManager : MemberManager
    {
        public IList<Member> Entities { get { return _entities; } }

        public IList<Member> Edited { get { return _edited; } }

        public IList<Member> New { get { return _new; } }

        public IList<Member> Deleted { get { return _deleted; } }

        public IList<Member> Managed { get { return _managed; } }

    }

    [TestClass]
    public class EntityManagerTests
    {

        /// <summary>
        /// Tests EntityManager.Save().
        /// </summary>
        [TestMethod]
        public void TestSave()
        {
            TestableMemberManager sm = new TestableMemberManager();

            // null parameter
            try
            {
                sm.Save(null);
                Assert.Fail();
            }
            catch (InvalidDataException) { }

            // new entity
            LocalStudent s1 = new LocalStudent(null);
            sm.Save(s1);
            Assert.AreEqual(1, s1.GetId());

            LocalStudent s2 = new LocalStudent(null);
            s2 = new LocalStudent(null);
            sm.Save(s2);
            Assert.AreEqual(2, s2.GetId());

            Assert.IsTrue(sm.New.Contains(s1));
            Assert.IsTrue(sm.New.Contains(s2));
            Assert.IsTrue(sm.Managed.Contains(s1));
            Assert.IsTrue(sm.Managed.Contains(s2));
            Assert.AreEqual(2, sm.New.Count);
            Assert.AreEqual(2, sm.Managed.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(0, sm.Edited.Count);
            Assert.AreEqual(0, sm.Deleted.Count);

            // editing new entity
            string newName = "Name_1";
            string newAddress = "Address_1";
            string newContact = "Contact_1";
            LocalStudent s3 = new LocalStudent();
            s3.AssignId((Int32) s2.GetId());
            s3.Name = newName;
            s3.Address = newAddress;
            s3.Contact = newContact;

            sm.Save(s3);
            Assert.AreEqual(newName, s2.Name);
            Assert.AreEqual(newAddress, s2.Address);
            Assert.AreEqual(newContact, s2.Contact);

            Assert.IsTrue(sm.New.Contains(s1));
            Assert.IsTrue(sm.New.Contains(s2));
            Assert.IsFalse(sm.New.Contains(s3));
            Assert.IsTrue(sm.Managed.Contains(s1));
            Assert.IsTrue(sm.Managed.Contains(s2));
            Assert.IsFalse(sm.Managed.Contains(s3));
            Assert.AreEqual(2, sm.New.Count);
            Assert.AreEqual(2, sm.Managed.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(0, sm.Edited.Count);
            Assert.AreEqual(0, sm.Deleted.Count);

            // editing entity in _entities
            sm.Entities.Add(s1);
            sm.New.Remove(s1);

            s3 = new LocalStudent();
            s3.AssignId((Int32) s1.GetId());
            s3.Name = newName;
            s3.Address = newAddress;
            s3.Contact = newContact;

            sm.Save(s3);
            Assert.AreEqual(newName, s1.Name);
            Assert.AreEqual(newAddress, s1.Address);
            Assert.AreEqual(newContact, s1.Contact);

            Assert.IsTrue(sm.Edited.Contains(s1));
            Assert.IsTrue(sm.New.Contains(s2));
            Assert.IsTrue(sm.Managed.Contains(s1));
            Assert.IsTrue(sm.Managed.Contains(s2));
            Assert.IsFalse(sm.Managed.Contains(s3));
            Assert.AreEqual(1, sm.New.Count);
            Assert.AreEqual(2, sm.Managed.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(1, sm.Edited.Count);
            Assert.AreEqual(0, sm.Deleted.Count);


            // editing entity in _edited
            newName = "Name_2";
            newAddress = "Address_2";
            newContact = "Contact_2";

            s3 = new LocalStudent();
            s3.AssignId((Int32) s1.GetId());
            s3.Name = newName;
            s3.Address = newAddress;
            s3.Contact = newContact;

            sm.Save(s3);
            Assert.AreEqual(newName, s1.Name);
            Assert.AreEqual(newAddress, s1.Address);
            Assert.AreEqual(newContact, s1.Contact);

            Assert.IsTrue(sm.Edited.Contains(s1));
            Assert.IsTrue(sm.New.Contains(s2));
            Assert.IsTrue(sm.Managed.Contains(s1));
            Assert.IsTrue(sm.Managed.Contains(s2));
            Assert.IsFalse(sm.Managed.Contains(s3));
            Assert.AreEqual(1, sm.New.Count);
            Assert.AreEqual(2, sm.Managed.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(1, sm.Edited.Count);
            Assert.AreEqual(0, sm.Deleted.Count);

            // editing entity in _deleted
            sm.Deleted.Add(s2);
            sm.New.Remove(s2);

            s3 = new LocalStudent();
            s3.AssignId((Int32) s2.GetId());
            s3.Name = newName;
            s3.Address = newAddress;
            s3.Contact = newContact;

            try
            {
                sm.Save(s3);
                Assert.Fail();
            }
            catch (InvalidDataException) { }

            Assert.IsTrue(sm.Edited.Contains(s1));
            Assert.IsTrue(sm.Deleted.Contains(s2));
            Assert.IsTrue(sm.Managed.Contains(s1));
            Assert.IsTrue(sm.Managed.Contains(s2));
            Assert.IsFalse(sm.Managed.Contains(s3));
            Assert.AreEqual(0, sm.New.Count);
            Assert.AreEqual(2, sm.Managed.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(1, sm.Edited.Count);
            Assert.AreEqual(1, sm.Deleted.Count);

        }

        /// <summary>
        /// Tests EntityManager.Delete().
        /// </summary>
        [TestMethod]
        public void TestDelete()
        {
            TestableMemberManager sm = new TestableMemberManager();
            LocalStudent s1 = new LocalStudent(1);
            LocalStudent s2 = new LocalStudent(2);
            LocalStudent s3 = new LocalStudent(3);
            LocalStudent s4 = new LocalStudent(4);
            LocalStudent s5;

            sm.New.Add(s1);
            sm.Entities.Add(s2);
            sm.Edited.Add(s3);
            sm.Deleted.Add(s4);
            sm.Managed.Add(s1);
            sm.Managed.Add(s2);
            sm.Managed.Add(s3);
            sm.Managed.Add(s4);

            // delete from _new
            s5 = new LocalStudent();
            s5.Assign(s1);
            sm.Delete(s5);

            Assert.IsFalse(sm.New.Contains(s1));
            Assert.IsTrue(sm.Deleted.Contains(s1));
            Assert.IsFalse(sm.Managed.Contains(s5));
            Assert.AreEqual(0, sm.New.Count);
            Assert.AreEqual(1, sm.Entities.Count);
            Assert.AreEqual(1, sm.Edited.Count);
            Assert.AreEqual(2, sm.Deleted.Count);
            Assert.AreEqual(4, sm.Managed.Count);

            // delete from _entities
            s5 = new LocalStudent();
            s5.Assign(s2);
            sm.Delete(s5);

            Assert.IsFalse(sm.Entities.Contains(s2));
            Assert.IsTrue(sm.Deleted.Contains(s2));
            Assert.IsFalse(sm.Managed.Contains(s5));
            Assert.AreEqual(0, sm.New.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(1, sm.Edited.Count);
            Assert.AreEqual(3, sm.Deleted.Count);
            Assert.AreEqual(4, sm.Managed.Count);

            // delete from _edited
            s5 = new LocalStudent();
            s5.Assign(s3);
            sm.Delete(s5);

            Assert.IsFalse(sm.Edited.Contains(s3));
            Assert.IsTrue(sm.Deleted.Contains(s3));
            Assert.IsFalse(sm.Managed.Contains(s5));
            Assert.AreEqual(0, sm.New.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(0, sm.Edited.Count);
            Assert.AreEqual(4, sm.Deleted.Count);
            Assert.AreEqual(4, sm.Managed.Count);

            // delete from _deleted
            s5 = new LocalStudent();
            s5.Assign(s4);
            try
            {
                sm.Delete(s5);
                Assert.Fail();
            }
            catch (InvalidDataException) { }

            Assert.IsTrue(sm.Deleted.Contains(s4));
            Assert.IsFalse(sm.Managed.Contains(s5));
            Assert.AreEqual(0, sm.New.Count);
            Assert.AreEqual(0, sm.Entities.Count);
            Assert.AreEqual(0, sm.Edited.Count);
            Assert.AreEqual(4, sm.Deleted.Count);
            Assert.AreEqual(4, sm.Managed.Count);
        }

        /// <summary>
        /// Test EntityManger.FindAll().
        /// </summary>
        [TestMethod]
        public void TestFindAll()
        {
            TestableMemberManager sm = new TestableMemberManager();
            LocalStudent s1 = new LocalStudent(1);
            LocalStudent s2 = new LocalStudent(2);
            LocalStudent s3 = new LocalStudent(3);
            LocalStudent s4 = new LocalStudent(4);

            sm.New.Add(s1);
            sm.Entities.Add(s2);
            sm.Edited.Add(s3);
            sm.Deleted.Add(s4);
            sm.Managed.Add(s1);
            sm.Managed.Add(s2);
            sm.Managed.Add(s3);
            sm.Managed.Add(s4);

            IList<Member> all = sm.FindAll();
            Assert.IsTrue(all.Contains(s1));
            Assert.IsTrue(all.Contains(s2));
            Assert.IsTrue(all.Contains(s3));
            Assert.IsFalse(all.Contains(s4));
        }

        /// <summary>
        /// Tests EntityMember.FindById().
        /// </summary>
        [TestMethod]
        public void TestFindById()
        {
            TestableMemberManager sm = new TestableMemberManager();
            LocalStudent s1 = new LocalStudent(1);
            LocalStudent s2 = new LocalStudent(2);
            LocalStudent s3 = new LocalStudent(3);
            LocalStudent s4 = new LocalStudent(4);

            sm.New.Add(s1);
            sm.Entities.Add(s2);
            sm.Edited.Add(s3);
            sm.Deleted.Add(s4);
            sm.Managed.Add(s1);
            sm.Managed.Add(s2);
            sm.Managed.Add(s3);
            sm.Managed.Add(s4);

            Assert.AreEqual(s1, sm.FindById(1));
            Assert.AreEqual(s2, sm.FindById(2));
            Assert.AreEqual(s3, sm.FindById(3));
            Assert.IsNull(sm.FindById(4));
        }
    }
}
